#include <strmif.h>
#include "mkvcontext.hpp"
#include "mkvstreamvideovpx.hpp"
#include <climits>
#include <cassert>
#ifdef _DEBUG
#include "odbgstream.hpp"
using std::endl;
using std::boolalpha;
#endif

namespace MkvMux
{

MkvStreamVideoVPx::VPxFrame::VPxFrame(IMediaSample* pSample) :
    m_pSample(pSample)
{
    assert(m_pSample);
    m_pSample->AddRef();
}


MkvStreamVideoVPx::VPxFrame::~VPxFrame()
{
    const ULONG n = m_pSample->Release();
    n;
}


ULONG MkvStreamVideoVPx::VPxFrame::curr_timecode_ms() const
{
    __int64 st, sp;
    const HRESULT hr = m_pSample->GetTime(&st, &sp);
    assert(SUCCEEDED(hr));    
    assert(st >= 0);    
    //assert((st % 10000) == 0);
    
    const __int64 ms = st / 10000;
    const ULONG result = static_cast<ULONG>(ms);
    
    return result;
}


bool MkvStreamVideoVPx::VPxFrame::IsKey() const
{
    return (m_pSample->IsSyncPoint() == S_OK);
}


ULONG MkvStreamVideoVPx::VPxFrame::size() const
{
    const long result = m_pSample->GetActualDataLength();
    assert(result >= 0);
    
    return result;
}


const BYTE* MkvStreamVideoVPx::VPxFrame::data() const
{
    BYTE* ptr;
    
    const HRESULT hr = m_pSample->GetPointer(&ptr);
    assert(SUCCEEDED(hr));
    assert(ptr);
    
    return ptr;
}



MkvStreamVideoVPx::MkvStreamVideoVPx(
    MkvContext& c,
    const AM_MEDIA_TYPE& mt) :
    MkvStreamVideo(c, mt)
{
}


void MkvStreamVideoVPx::WriteTrackName()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID2(0x536E);   //name
    f.Write1UTF8(L"VP8 video stream");  //TODO
}



void MkvStreamVideoVPx::WriteTrackCodecID()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID1(0x86);  //Codec ID
    f.Write1String("V_ON2VP8");  //TODO
}


void MkvStreamVideoVPx::WriteTrackCodecName()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID3(0x258688);  //Codec Name
    f.Write1UTF8(L"VP8");  //TODO
}


void MkvStreamVideoVPx::WriteTrackSettings()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID1(0xE0);  //video settings
    
    //allocate 2 bytes of storage for size of settings
    const __int64 begin_pos = f.SetPosition(2, STREAM_SEEK_CUR);    
    
    const BITMAPINFOHEADER& bmih = GetBitmapInfoHeader();
    assert(bmih.biSize >= sizeof(BITMAPINFOHEADER));
    assert(bmih.biWidth > 0);
    assert(bmih.biWidth <= USHRT_MAX);
    assert(bmih.biHeight > 0);
    assert(bmih.biHeight <= USHRT_MAX);
    
    const USHORT width = static_cast<USHORT>(bmih.biWidth);
    const USHORT height = static_cast<USHORT>(bmih.biHeight);

    f.WriteID1(0xB0);  //width
    f.Write1UInt(2);
    f.Serialize2UInt(width);
    
    f.WriteID1(0xBA);  //height
    f.Write1UInt(2);
    f.Serialize2UInt(height);
    
    const float framerate = GetFramerate();
    
    if (framerate > 0)
    {
        f.WriteID3(0x2383E3);  //frame rate
        f.Write1UInt(4);
        f.Serialize4Float(framerate);    
    }

    const __int64 end_pos = f.GetPosition();
    
    const __int64 size_ = end_pos - begin_pos;
    assert(size_ <= USHRT_MAX);
    
    const USHORT size = static_cast<USHORT>(size_);

    f.SetPosition(begin_pos - 2);
    f.Write2UInt(size);
    
    f.SetPosition(end_pos);
}


HRESULT MkvStreamVideoVPx::Receive(IMediaSample* pSample)
{
    assert(pSample);

    VPxFrame* const pFrame = new (std::nothrow) VPxFrame(pSample);
    assert(pFrame);  //TODO
    
#if 0
    odbgstream os;
    os << "mkvmux::vpx::receive: time[ms]=" << pFrame->curr_timecode_ms()
       << " key=" << boolalpha << pFrame->IsKey()
       << endl;
#endif
   
    assert(!m_vframes.empty() || pFrame->IsKey());
           
    m_vframes.push_back(pFrame);
    
    m_context.NotifyVideoFrame(this, pFrame);

    return S_OK;
}


int MkvStreamVideoVPx::EndOfStream()
{
    return m_context.NotifyVideoEOS(this);
}


LONG MkvStreamVideoVPx::last_timecode_ms() const
{
    if (m_vframes.empty())
        return -1;
        
    VideoFrame* const pFrame = m_vframes.back();
    assert(pFrame);
    
    return pFrame->curr_timecode_ms();
}


}  //end namespace MkvMux

